﻿using System;
using System.Linq;
using Contact.Contracts.Models.Worktime;
using EFDataModel;

namespace Contact.DAL.Models.Worktime
{
    /// <summary>
    /// Funktionen zum Verwalten der Arbeitszeiteinstellungen
    /// </summary>
    public class WorktimeRepository : IWorktime
    {
        #region Interface Members
        /// <summary>
        /// Der Aktuelle ModelContainer
        /// </summary>
        public EFModelContainer CurrentModel { get; set; }

        /// <summary>
        /// Ermittelt alle Worktime Einträge für den übergebenen Zeitraum
        /// </summary>
        /// <param name="userId">Die UserId nach der gesucht werden soll</param>
        /// <param name="startDay">Das Startdatum von es aus die Arbeitszeiteinträge ermittelnt werden sollen</param>
        /// <param name="endDay">Das Endedatum zu dem alle Arbeitszeiteinträge ermittelt werden sollen.</param>
        public IQueryable<EFWorktime> GetWorktime(long userId, DateTime startDay, DateTime endDay)
        {
            startDay = startDay.Date;
            endDay = endDay.Date;

            //Den passende Arbeitzeiteintrag für das Datum und den User zurückgeben. Beides muss zusammenpassen!
            var setting = from set in CurrentModel.EFWorktimeSet
                          where set.EntryDate >= startDay &&
                                set.EntryDate <= endDay &&
                                set.EFAuthenticationID == userId
                          select set;
            return setting;
        }

        /// <summary>
        /// Löschen des übergebenen Arbeitszeiteintrags aus der Datenbank
        /// </summary>
        /// <param name="worktimeEntryId">ArbeitszeiteintragsId</param>
        /// <returns>TRUE->Löschen erfolgreich | FALSE->Fehler beim Löschen</returns>
        public bool DeleteWorktimeEntry(long worktimeEntryId)
        {
            //Den Eintrag heraussuchen der aus der DB entfernt werden soll.
            var entry = CurrentModel.EFBaseMenge.OfType<EFWorktimeEntry>().FirstOrDefault(p => p.ID == worktimeEntryId);
            if (entry != null)
            {
                try
                {
                    CurrentModel.EFBaseMenge.Remove(entry);
                    CurrentModel.SaveChanges();
                    return true;
                }
                catch (Exception exception)
                {
                    EFDebugging.Debugging.WriteLog(string.Format("Fehler beim Löschen des DS {0} für WorktimeEntry", worktimeEntryId), exception);
                }
            }

            return false;
        }

        /// <summary>
        /// Ermitteln eines Arbeitszeittages anhand der übergebenen ID
        /// </summary>
        /// <param name="userId">die zugehörige Userid für den Arbeitszeiteintrag</param>
        /// <param name="worktimeId">Die Id des Arbeitszeiteintrages der herausgesucht werden soll</param>
        public EFWorktime GetWorktime(long userId, long worktimeId)
        {
            //Den passende Arbeitzeiteintrag für die ArbeitszeitId und den User zurückgeben. Beides muss zusammenpassen!
            var setting = (from set in CurrentModel.EFWorktimeSet
                           where set.Id == worktimeId && set.EFAuthenticationID == userId
                           select set).FirstOrDefault();
            return setting;
        }

        /// <summary>
        /// Ermitteln eines Arbeitszeit Eintrags anhand der übergebenen ID
        /// </summary>
        /// <param name="userId">die zugehörige Userid für den Arbeitszeiteintrag</param>
        /// <param name="date">der Tag für den der Arbeitszeiteintrag geladen werden soll</param>
        public EFWorktime GetWorktime(long userId, DateTime date)
        {
            //Den passende Arbeitzeiteintrag für das Datum und den User zurückgeben. Beides muss zusammenpassen!
            var setting = (from set in CurrentModel.EFWorktimeSet
                           where set.EntryDate == date && set.EFAuthenticationID == userId
                           select set).FirstOrDefault();
            return setting;
        }

        /// <summary>
        /// Ermitteln eines Arbeitszeit Eintrags anhand der übergebenen ID
        /// </summary>
        /// <param name="userId">die zugehörige Userid für den Arbeitszeiteintrag</param>
        public IQueryable<EFWorktime> GetWorktimeList(long userId)
        {
            //Den passende Arbeitzeiteintrag für das Datum und den User zurückgeben. Beides muss zusammenpassen!
            var setting = from set in CurrentModel.EFWorktimeSet
                          where set.EFAuthenticationID == userId
                          select set;
            return setting;
        }

        /// <summary>
        /// Ermitteln eines Arbeitszeit Eintrags anhand der übergebenen ID
        /// </summary>
        /// <param name="userId">die zugehörige Userid für den Arbeitszeiteintrag</param>
        /// <param name="worktimeEntryId">Die Id des Arbeitszeiteintrages der herausgesucht werden soll</param>
        public EFWorktimeEntry GetWorktimeEntry(long userId, long worktimeEntryId)
        {
            //Den passende Arbeitzeiteintrag für die ArbeitszeitId und den User zurückgeben. Beides muss zusammenpassen!
            var setting = (from set in CurrentModel.EFBaseMenge.OfType<EFWorktimeEntry>()
                           where set.ID == worktimeEntryId && set.EFWorktime.EFAuthenticationID == userId
                           select set).FirstOrDefault();
            return setting;
        }

        /// <summary>
        /// Speichern/Hinzufügen des übergebenen Arbeitszeiteintrages
        /// </summary>
        /// <param name="entry">der Arbeitszeiteintrag der gespeichert werden soll</param>
        public bool SaveWorktimeEntry(EFWorktimeEntry entry, EFWorktime worktime)
        {
            try
            {
                //Prüfen ob der Eintrag bereits existierte, wenn nicht
                //wird dieser erst der DB hinzugefügt.
                if (entry.ID == 0)
                {
                    CurrentModel.EFBaseMenge.Add(entry);
                }

                //Und unserem Worktimeobjekt angehängt.
                if (worktime != null)
                {
                    worktime.EFWorktimeEntry.Add(entry);
                }

                CurrentModel.SaveChanges();
                return true;
            }
            catch (Exception exception)
            {
                EFDebugging.Debugging.WriteLog("Fehler beim Speichern des Worktimeeintrags", exception);
            }

            return false;
        }

        /// <summary>
        /// Speichern des übergebenen Wortime Objektes
        /// </summary>
        /// <param name="worktime">Die worktime die gespeichert werden soll</param>
        public bool SaveWorktimeEntry(EFWorktime worktime)
        {
            try
            {
                CurrentModel.SaveChanges();
                return true;
            }
            catch (Exception exception)
            {
                EFDebugging.Debugging.WriteLog("Fehler beim Speichern der Worktime", exception);
            }

            return false;
        }

        /// <summary>
        /// Anlegen des übergebenen Arbeitszeiteintrages
        /// </summary>
        /// <param name="entry">der Arbeitszeiteintrag der angelegt werden soll</param>
        public bool AddWorktimeEntry(EFWorktimeEntry entry)
        {
            return SaveWorktimeEntry(entry, null);
        }

        /// <summary>
        /// Ermittelt die Arbeitszeiteinträge für den übergebenen Tag
        /// </summary>
        /// <param name="day">Der Tag für den die Arbeitszeiteinträge ermittelt werden sollen</param>
        /// <param name="userId">Die Userid für die die Daten herausgesucht werden sollen </param>
        public IQueryable<EFWorktimeEntry> GetWorktimeEntriesByDay(long userId, DateTime day)
        {
            var worktimes = from entry in CurrentModel.EFBaseMenge.OfType<EFWorktimeEntry>()
                            where entry.EFWorktime.EFAuthenticationID == userId && entry.EFWorktime.EntryDate == day
                            orderby entry.StartTime
                            select entry;
            return worktimes;
        }

        /// <summary>
        /// Ermittelt die Arbeitszeiteinträge für den übergebenen Tag
        /// </summary>
        /// <param name="userId">Die Userid für die die Daten herausgesucht werden sollen </param>
        /// <param name="type">Der Arbeitstagtyp nach dem gesucht werden soll, z.B. Arbeitszeit, Urlaub, ... </param>
        /// <param name="day">Der Tag für den die Arbeitszeiteinträge ermittelt werden sollen</param>
        public IQueryable<EFWorktimeEntry> GetWorktimeEntriesByDayAndType(long userId, EWorktimeType type, DateTime day)
        {
            return GetWorktimeEntriesByDay(userId, day).Where(p => p.WorkingtimeType == type);
        }

        /// <summary>
        /// Ermittelt alle Arbeitseinträge die als Offizieller Feiertag gekennzeichnet sind.
        /// </summary>
        /// <param name="userId">Die Userid für die die Daten herausgesucht werden sollen </param>
        /// <param name="day">Der Tag für den die Arbeitszeiteinträge ermittelt werden sollen</param>
        public IQueryable<EFWorktime> GetWorktimeByMonth(long userId, DateTime day)
        {
            //Das passende Start und endedatum ermitteln
            DateTime startDate = new DateTime(day.Year, day.Month, 1);
            startDate = startDate.AddDays(-1);
            DateTime endDate = new DateTime(day.Year, day.Month, 1);
            endDate = endDate.AddMonths(1);

            //Alle Einträge für den Monat heraussuchen
            return GetWorktimeList(userId).Where(p => p.EntryDate > startDate && p.EntryDate < endDate);
        }

        /// <summary>
        /// Ermittelt die letzten XX (count) Einträge an Arbeitszeiteinträgen für den übergebenen User.
        /// </summary>
        /// <param name="userId">Die UserId nach der gesucht werden soll</param>
        /// <param name="count">Die Anzahl der Einträge die herausgesucht werden sollen</param>
        public IQueryable<EFWorktimeEntry> GetLastWorktimeEntries(long userId, int count)
        {
            //Die letzten X Arbeitszeiteinträge für den User ermitteln.
            var worktimes = (from entry in CurrentModel.EFBaseMenge.OfType<EFWorktimeEntry>()
                             where entry.EFWorktime.EFAuthenticationID == userId
                             orderby entry.StartTime
                             select entry).Take(count);
            return worktimes;
        }

        /// <summary>
        /// Gibt alle Arbeitszeiteinträge für den übergebenen User zurück
        /// </summary>
        /// <param name="userId">Die UserId für die die Arbeitszeiteinträge heruntergeladen werden sollen.</param>
        public IQueryable<EFWorktimeEntry> GetAllWorktimeEntries(long userId)
        {
            var worktimes = from entry in CurrentModel.EFBaseMenge.OfType<EFWorktimeEntry>()
                            where entry.EFWorktime.EFAuthenticationID == userId
                            orderby entry.StartTime
                            select entry;
            return worktimes;
        }
        #endregion

        #region Konstruktor
        /// <summary>
        /// Initialize
        /// </summary>
        /// <param name="efModelContainer">Der Modelcontainer der für die Abfragen verwendet werden soll.</param>
        public WorktimeRepository(EFModelContainer efModelContainer)
        {
            CurrentModel = efModelContainer;
        }
        #endregion
    }
}
